﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TrainStation.Tracks;
using TrainStation.TrainModules;

namespace TrainStation
{
    public class RailRoad
    {
        private ITrack road;
        public ITrack Road
        {
            get { return road; }
            set { road = value; }
        }

        Train train;
        public RailRoad( Train train )
        {
            this.train = train;
        }

        public double Length
        {
            get
            {
                double length = 0;
                ITrack next = road;
                while (next != null)
                {
                    length += road.Length;
                    next = next.Next;
                }

                return length;
            }
        }

        public void StartTrains()
        {
            train.Go();
        }

        public void UpdateStates( double deltatime )
        {
            //Currently only room for one train
            switch (train.CurrentState)
            {
                case TrainState.Stopped:
                    Log("Stopped");
                    HandleStoppedState(deltatime, train);
                    break;
                case TrainState.Running:
                    Log("Running");
                    HandleRunningState(deltatime, train);
                    break;
                case TrainState.Accelerating:
                    Log("Accelerating");
                    HandleAcceleratingState(deltatime, train);
                    break;
                case TrainState.Decelerating:
                    Log("Decelerating");
                    HandleDeceleratingState(deltatime, train);
                    break;
                default:
                    break;
            }

            Log("updates position");
            UpdatePosition(deltatime, train);
        }

        private void UpdatePosition( double deltatime, Train train )
        {
            train.Position += ( train.CurrentSpeed / 1000 ) * deltatime;
            Log("new position: " + train.Position + ", speed: " + train.CurrentSpeed);

            if (train.Position >= train.CurrentTrack.Length)
            {
                Log("Next track");
                double difference = Math.Abs(train.Position - train.CurrentTrack.Length);

                //TODO dette virker højst sansynligt ikke..
                //placeret i et do while loop for potentielt at kunne hoppe flere tracks frem ved hver opdatering.
                //ikke umiddelbart noget der bliver et problem medmindre tracks er meget korte og toget kører super stærkt
                do
                {
                    //Moves to next track
                    //Resets
                    train.CurrentTrack.IsBlocked = false;
                    train.CurrentTrack.Train = null;

                    train.CurrentTrack = train.CurrentTrack.Next;

                    //inits
                    train.CurrentTrack.Train = train;
                    train.CurrentTrack.IsBlocked = true;

                    //Resets position
                    train.Position = difference;
                }
                while (difference > train.CurrentTrack.Length);
            }
        }

        #region States
        private void HandleDeceleratingState( double deltatime, Train train )
        {
            if (train.CurrentSpeed < 0.2)
            {
                train.CurrentSpeed = 0;
                train.CurrentState = TrainState.Stopped;
            }
            else
            {
                train.CurrentSpeed -= ( train.CurrentSpeed / 1000 ) * deltatime;
            }
        }

        private void HandleStoppedState( double deltatime, Train train )
        {
            if (train.CurrentSpeed > 0)
            {
                train.CurrentState = TrainState.Decelerating;
            }
        }

        private void HandleAcceleratingState( double deltatime, Train train )
        {
            //Todo this will most likely not work at all..
            if (Math.Abs(train.CurrentSpeed - train.RequestedSpeed) < 0.2)
            {
                train.CurrentSpeed = train.RequestedSpeed;
                train.CurrentState = TrainState.Running;
                return;
            }

            train.CurrentSpeed += ( train.Acceleration / 1000 ) * deltatime;
            //limits the train speed
            train.CurrentSpeed = Clamp(train.CurrentSpeed, 0, train.MaxSpeed);
        }

        private void HandleRunningState( double deltatime, Train train )
        {
            if (Math.Abs(train.CurrentSpeed - train.RequestedSpeed) > 0.2)
            {
                train.CurrentState = TrainState.Accelerating;
            }
        }

        #endregion

        List<ITrack> tracks;
        public List<ITrack> Tracks
        {
            get
            {
                if (tracks == null)
                {
                    tracks = new List<ITrack>();
                    TraverseRailRoad(road);
                }
                return tracks;
            }
        }

        void TraverseRailRoad( ITrack track )
        {
            tracks.Add(track);
            if (!Tracks.Contains(track.Next))
            {
                TraverseRailRoad(track.Next);
            }
        }

        public double Clamp( double value, double Min, double Max )
        {
            if (value < Min)
            {
                return Min;
            }
            else if (value > Max)
            {
                return Max;
            }

            return value;
        }

        public void Log( string m )
        {
            Console.WriteLine(m);
        }
    }
}
